from common.models.queue import OauthAccessToken
from common.libs.Helper import getCurrentDate
import hashlib, requests, uuid
import xml.etree.ElementTree as ET
from application import app, db
import socket, json
import time, datetime

config_mina = app.config['PAY_INFO']
notify_url = app.config['APP']['domain'] + config_mina['callback_url']

def get_nonce_str():
    '''
    获取随机字符串
    :return:
    '''
    return str(uuid.uuid4()).replace('-', '')

class WeChatService(object):
    """
    https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_4&index=3
    """

    def __init__(self, pay_data=None):
        self.url = 'https://api.mch.weixin.qq.com/pay/unifiedorder'
        self.appid = config_mina['appid']  # 微信分配的小程序ID
        self.mch_id = config_mina['mch_id']  # 商户号
        self.spbill_create_ip = socket.gethostbyname(socket.gethostname())  # 获取本机ip
        self.merchant_key = config_mina['merchant_key']  # 商户的KEY
        self.pay_data = pay_data

    def create_sign(self, pay_data):
        """
        生成签名
        https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=4_3
        :param pay_data:
        :return:
        """
        # 拼接stringA
        string_a = '&'.join(["{0}={1}".format(k, pay_data.get(k)) for k in sorted(pay_data)])
        # 拼接key
        string_sign_temp = '{0}&key={1}'.format(string_a, self.merchant_key).encode('utf-8')
        # md5签名
        sign = hashlib.md5(string_sign_temp).hexdigest()
        return sign.upper()

    def get_pay_info(self):
        """
        支付统一下单
        :return:
        """
        post_data = {
            'appid': self.appid,  # 小程序ID
            'mch_id': self.mch_id,  # 商户号
            'nonce_str': get_nonce_str(),  # 随机字符串
            'body': self.pay_data.get('body'),  # 商品描述
            'out_trade_no': self.pay_data.get('out_trade_no'),  # 商户订单号
            'total_fee': self.pay_data.get('total_fee'),  # 订单总金额，单位为分
            'spbill_create_ip': self.spbill_create_ip,  # 终端 IP
            'notify_url': notify_url,  # 通知地址
            'trade_type': self.pay_data.get('trade_type')
            # 'attach': self.pay_data.get('attach'),  # 附加数据
        }
        sign = self.create_sign(post_data)
        post_data['sign'] = sign

        xml = self.dict_to_xml(post_data)

        # 统一下单接口请求
        headers = {'Content-Type': 'application/xml'}
        r = requests.post(self.url, data=xml.encode("utf-8"), headers=headers)
        r.encoding = "utf-8"
        res = self.xml_to_dict(r.text)

        err_code_des = res.get('err_code_des')
        # 出错信息
        if err_code_des:
            return {'code': 40001, 'msg': err_code_des}

        prepay_id = res.get('prepay_id')
        return self.re_sign(post_data, prepay_id)

    def re_sign(self, post_data, prepay_id):
        """
        再次对返回的数据签名
        https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_7&index=3
        :param post_data:
        :param prepay_id:
        :return:
        """
        pay_sign_data = {
            'appId': self.appid,
            'timeStamp': str(int(time.time())),
            'nonceStr': post_data.get('nonce_str'),
            'package': 'prepay_id={0}'.format(prepay_id),
            'signType': 'MD5',
        }
        pay_sign = self.create_sign(pay_sign_data)
        pay_sign_data['paySign'] = pay_sign

        # 保存prepay_id为了后面发模板消息
        pay_sign_data['prepay_id'] = prepay_id
        return pay_sign_data

    def dict_to_xml(self, dict_data):
        '''
        dict to xml
        :param dict_data:
        :return:
        '''
        xml = ["<xml>"]
        for k, v in dict_data.items():
            xml.append("<{0}>{1}</{0}>".format(k, v))
        xml.append("</xml>")
        return "".join(xml)

    def xml_to_dict(self, xml_data):
        '''
        xml to dict
        :param xml_data:
        :return:
        '''
        xml_dict = {}
        root = ET.fromstring(xml_data)
        for child in root:
            xml_dict[child.tag] = child.text
        return xml_dict

    def getAuthToken(self):
        token = None
        # 如果存在没有过期的则使用没有过期的access_token
        token_info = OauthAccessToken.query.filter(OauthAccessToken.expired_time >= getCurrentDate()).first()
        if token_info:
            token = token_info.access_token
            return token
        appid = app.config['MINA_INFO']['appid']
        secret = app.config['MINA_INFO']['secret']
        url = f'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={appid}&secret={secret}'
        response = requests.get(url=url)
        if response.status_code != 200 or not response.text:
            return token
        content = json.loads(response.text)
        now = datetime.datetime.now()
        expired_time = now + datetime.timedelta(seconds=content['expires_in'])
        model_token = OauthAccessToken()
        model_token.access_token = content['access_token']
        model_token.expired_time = expired_time.strftime('%Y-%m-%d %H:%M:%S')
        model_token.created_time = getCurrentDate()
        model_token.save()
        return content['access_token']
